package com.jade.bss.admin.grant.impl;

import java.security.Principal;
import java.util.ArrayList;
import java.util.List;
import javax.ejb.TransactionAttribute;
import javax.inject.Inject;
import javax.inject.Named;

import com.jade.bss.admin.AdminConstants;
import com.jade.bss.admin.AdminException;
import com.jade.bss.admin.grant.GrantDao;
import com.jade.bss.admin.grant.GrantEntry;
import com.jade.bss.admin.grant.GrantManager;
import com.jade.bss.admin.permission.PermissionEntry;
import com.jade.bss.admin.permission.PermissionManager;
import com.jade.bss.admin.principal.PrincipalFactory;
import com.jade.bss.admin.principal.SimplePrincipal;
import com.jade.bss.admin.role.RoleEntry;
import com.jade.bss.admin.role.RoleManager;
import org.springframework.transaction.annotation.Transactional;

/**
 * GrantManager的接口实现，通过GrantDao进行数据访问。
 *
 * @author sky
 * @version 1.0 13-4-18 上午9:47
 */
@Named ("bss_grantManager")
public class GrantManagerImpl implements GrantManager
{
    protected GrantDao grantDao;
    protected PrincipalFactory principalFactory;
    protected PermissionManager permissionManager;
    protected RoleManager roleManager;

    @Inject
    public void setGrantDao(@Named ("bss_grantDao") GrantDao roleDao)
    {
        this.grantDao = roleDao;
    }

    @Inject
    public void setPrincipalFactory(@Named ("bss_principalFactory") PrincipalFactory principalFactory)
    {
        this.principalFactory = principalFactory;
    }

    @Inject
    public void setPermissionManager(@Named ("bss_permissionManager") PermissionManager permissionManager)
    {
        this.permissionManager = permissionManager;
    }

    @Inject
    public void setRoleManager(@Named ("bss_roleManager") RoleManager roleManager)
    {
        this.roleManager = roleManager;
    }

    public List<PermissionEntry> getPermissions(Principal principal)
    {
        String principalType = principalFactory.getPrincipalType(principal);
        List<GrantEntry> grants = grantDao.listGrant(principalType, principal.getName());
        List<PermissionEntry> permissions = new ArrayList<PermissionEntry>(grants.size());
        for (GrantEntry grant : grants) {
            if (grant.getEntryType().equalsIgnoreCase(GrantEntry.ENTRY_TYPE_ROLE)) {
                SimplePrincipal p = new SimplePrincipal();
                p.setName(String.valueOf(grant.getEntryId()));
                p.setType(SimplePrincipal.TYPE_ROLE);
                permissions.addAll(getPermissions(p));
            }
            else {
                permissions.add(permissionManager.getPermission(grant.getEntryId()));
            }
        }
        return permissions;
    }

    public List<PermissionEntry> getPermissions(Principal[] principals)
    {
        List<PermissionEntry> permissions = new ArrayList<PermissionEntry>();
        for (Principal principal : principals) {
            List<PermissionEntry> prePermissions = getPermissions(principal);
            for (PermissionEntry permission : prePermissions) {
                if (!permissions.contains(permission)) {
                    permissions.add(permission);
                }
            }
        }
        return permissions;
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Exception.class)
    public void grantPermission(Principal principal, int permissionId)
            throws AdminException
    {
        PermissionEntry permission = permissionManager.getPermission(permissionId);
        if (permission == null) {
            throw new AdminException(AdminConstants.ERR_PERM_NOT_EXISTED, "permission not existed", null, permissionId);
        }
        grantPermission(principal, permission);
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Exception.class)
    public void setPermissions(Principal principal, int[] permissionIds)
            throws AdminException
    {
        revokePermissions(principal);
        grantPermissions(principal, permissionIds);

    }

    @TransactionAttribute
    @Transactional (rollbackFor = Exception.class)
    public void grantPermissions(Principal principal, int[] permissionIds)
            throws AdminException
    {
        for (int permissionId : permissionIds) {
            grantPermission(principal, permissionId);
        }
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Exception.class)
    public void grantPermission(Principal principal, PermissionEntry permission)
            throws AdminException
    {
        String principalType = principalFactory.getPrincipalType(principal);
        // check该身份是否已经具备该权限
        GrantEntry grant = grantDao.getGrant(principalType, principal.getName(), GrantEntry.ENTRY_TYPE_PERM,
                                             permission.getId());
        if (grant != null) {
            throw new AdminException(AdminConstants.ERR_GRANT_PERM_EXISTED,
                                     "Permission has already granted to the principal", null, principalType,
                                     principal.getName(), permission.getId());
        }
        grant = new GrantEntry();
        grant.setPrincipalType(principalType);
        grant.setPrincipalName(principal.getName());
        grant.setEntryType(GrantEntry.ENTRY_TYPE_PERM);
        grant.setEntryId(permission.getId());
        //grant.setPermission(permission);
        grantDao.addGrant(grant);
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Exception.class)
    public void revokePermissions(Principal principal)
            throws AdminException
    {
        String principalType = principalFactory.getPrincipalType(principal);
        grantDao.removeGrant(principalType, principal.getName(), GrantEntry.ENTRY_TYPE_PERM);
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Exception.class)
    public void revokePermission(Principal principal, int permissionId)
            throws AdminException
    {
        String principalType = principalFactory.getPrincipalType(principal);
        grantDao.removeGrant(principalType, principal.getName(), GrantEntry.ENTRY_TYPE_PERM, permissionId);
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Exception.class)
    public void revokePermissions(Principal principal, int[] permissionIds)
            throws AdminException
    {
        for (int permissionId : permissionIds) {
            revokePermission(principal, permissionId);
        }
    }

    public List<RoleEntry> getRole(Principal principal)
            throws AdminException
    {
        String principalType = principalFactory.getPrincipalType(principal);
        List<GrantEntry> grants = grantDao.listGrant(principalType, principal.getName(), SimplePrincipal.TYPE_ROLE);
        List<RoleEntry> roles = new ArrayList<RoleEntry>(grants.size());
        for (GrantEntry grant : grants) {
            roles.add(roleManager.getRole(grant.getEntryId()));
        }
        return roles;
    }

    public List<Principal> listPrincipal(int roleId)
    {
        List<GrantEntry> list = grantDao.listGrantFromEntry(SimplePrincipal.TYPE_ROLE, String.valueOf(roleId));
        List<Principal> principalList = new ArrayList<Principal>();
        for (GrantEntry entry : list) {
            Principal principal = principalFactory.createPrincipal(entry.getPrincipalType(), entry.getPrincipalName());
            principalList.add(principal);
        }
        return principalList;
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Exception.class)
    public void grantRole(Principal principal, int roleId)
            throws AdminException
    {
        //get role
        RoleEntry role = roleManager.getRole(roleId);
        if (role == null) {
            throw new AdminException(AdminConstants.ERR_ROLE_NOT_EXISTED, null, roleId);
        }
        String principalType = principalFactory.getPrincipalType(principal);
        // check if the principal has already contained the role
        GrantEntry entry = grantDao.getGrant(principalType, principal.getName(), GrantEntry.ENTRY_TYPE_ROLE, roleId);
        if (entry != null) {
            throw new AdminException(AdminConstants.ERR_ROLE_PRINCIPAL_EXISTED, null, principalType,
                                     principal.getName(), roleId);
        }
        entry = new GrantEntry();
        entry.setPrincipalType(principalType);
        entry.setPrincipalName(principal.getName());
        entry.setEntryType(GrantEntry.ENTRY_TYPE_ROLE);
        entry.setEntryId(roleId);
        grantDao.addGrant(entry);
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Exception.class)
    public void setRoles(Principal principal, int[] roleIds)
            throws AdminException
    {
        revokeRoles(principal);
        grantRoles(principal, roleIds);
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Exception.class)
    public void grantRoles(Principal principal, int[] roleIds)
            throws AdminException
    {
        for (int roleId : roleIds) {
            grantRole(principal, roleId);
        }
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Exception.class)
    public void revokeRole(Principal principal, int roleId)
            throws AdminException
    {
        String principalType = principalFactory.getPrincipalType(principal);
        grantDao.removeGrant(principalType, principal.getName(), GrantEntry.ENTRY_TYPE_ROLE, roleId);
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Exception.class)
    public void revokeRoles(Principal principal, int[] roleIds)
            throws AdminException
    {
        for (int roleId : roleIds) {
            revokeRole(principal, roleId);
        }
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Exception.class)
    public void revokeRoles(Principal principal)
            throws AdminException
    {
        String principalType = principalFactory.getPrincipalType(principal);
        grantDao.removeGrant(principalType, principal.getName(), GrantEntry.ENTRY_TYPE_ROLE);
    }

    @TransactionAttribute
    @Transactional (rollbackFor = Exception.class)
    public void revokePrincipals(int roleId)
            throws AdminException
    {
        grantDao.removeGrantFromEntry(GrantEntry.ENTRY_TYPE_ROLE, roleId);
    }
}
